#pragma once

#include "appHeartbeatMsgObj.hpp"
#include "devCyclicReportMsgObj.hpp"
#include "devListAckMsgObj.hpp"
#include "devStatReportMsgObj.hpp"
#include "deviceManager.hpp"
#include "mqttClient.hpp"
#include "mqttServer.hpp"
#include "pollDeviceServer.hpp"
#include "requestDevListMsgObj.hpp"
#include "ruleManager.hpp"
#include <memory>

class MainProcess {
private:
    std::shared_ptr<MqttClient> _mqttClient;
    std::shared_ptr<DeviceManager> _deviceManager;
    std::shared_ptr<MqttServer> _mqttService;
    std::shared_ptr<RuleManager> _ruleManager;
    std::shared_ptr<PollDeviceServer> _pollDeviceServer;

private:
    void onReadPoint(void *dev, Point *p, const ByteStream &oldData, const ByteStream &newData) {
        // std::cout << "read point --> " << p->name() << std::endl;
        // std::cout << "point has change strategy ? " << p->hasChangeStrategy() << std::endl;
        // std::cout << "old value --> " << std::any_cast<double>(p->pointValue(oldData)) << std::endl;
        // std::cout << "new value --> " << std::any_cast<double>(p->pointValue(newData)) << std::endl;

        if (oldData != newData && p->hasChangeStrategy()) {
            _mqttClient->publish("v1/dev/dev_dlt698/0/point/report/change", nlohmann::json(Protocol::DevCyclicReportMsgObject(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(), static_cast<Device *>(dev)->id(), p->name(), p->pointValue(newData))).dump());
        }
    }

    void onLoopRule(std::shared_ptr<Device> d, Point *p) {
        if (p->hasData() && d->isOnline()) {
            auto curMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
            auto ms = curMs % p->loopTime();
            _mqttClient->publish("v1/dev/dev_dlt698/0/point/report/cyclic", nlohmann::json(Protocol::DevCyclicReportMsgObject(curMs - ms, d->id(), p->name(), p->value())).dump());
        }
    }

    void onAddDevice(std::shared_ptr<Device> d) {
        d->registerReadCallback(&MainProcess::onReadPoint, this);
        d->registerOnlineCallback(&MainProcess::onDeviceOnline, this);
        d->registerOfflineCallback(&MainProcess::onDeviceOffline, this);

        for (auto &point : *d) {
            if (point.second.hasLoopStrategy() && point.second.hasLoopTime()) {
                _ruleManager->everyStartWith(point.second.loopUuid(), std::chrono::milliseconds(point.second.loopTime()), &MainProcess::onLoopRule, this, d, &point.second);
            }
        }

        _pollDeviceServer->push_if(d->id());
    }

    void onDelDevice(std::shared_ptr<Device> d) {
        for (auto &point : *d) {
            if (point.second.hasLoopStrategy()) {
                _ruleManager->removeRule(point.second.loopUuid());
            }
        }
        _pollDeviceServer->pop_if(d->id());
    }

    void requestRlistThread(void) {
        while (!Protocol::DevListAckMsgObject::isGetListAck()) {
            std::this_thread::sleep_for(std::chrono::seconds(3));
            if (Protocol::DevListAckMsgObject::isGetListAck()) {
                break;
            }
            _mqttClient->publish("v1/dev/dev_dlt698/sdev_manager/list/get", nlohmann::json(Protocol::RequestDevListMsgObject()).dump());
        }
    }

    void heartbeatThread(void) {
        while (true) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            _mqttClient->publish("v1/app/dev_dlt698/0/point/report", nlohmann::json(Protocol::AppHeartbeatMsgObject()).dump());
            std::this_thread::sleep_for(std::chrono::seconds(19));
        }
    }

    void onDeviceOnline(Device *d) {
        _mqttClient->publish("v1/dev/dev_modbusrtu/0/state/report", nlohmann::json(Procotol::DeviceStatReportMsgObject(d->id(), true)).dump());
    }

    void onDeviceOffline(Device *d) {
        _mqttClient->publish("v1/dev/dev_modbusrtu/0/state/report", nlohmann::json(Procotol::DeviceStatReportMsgObject(d->id(), false)).dump());
    }

public:
    MainProcess()
        : _mqttClient(std::make_shared<MqttClient>("dev_dlt698")),
          _deviceManager(std::make_shared<DeviceManager>()),
          _mqttService(std::make_shared<MqttServer>(_mqttClient, _deviceManager)),
          _ruleManager(std::make_shared<RuleManager>(12)),
          _pollDeviceServer(std::make_shared<PollDeviceServer>(_deviceManager)) {
        std::thread(&MainProcess::requestRlistThread, this).detach();
        std::thread(&MainProcess::heartbeatThread, this).detach();

        _mqttService->start();
        _pollDeviceServer->start();
        _deviceManager->registerOnAddDevice(&MainProcess::onAddDevice, this, std::placeholders::_1);
        _deviceManager->registerOnDelDevice(&MainProcess::onDelDevice, this, std::placeholders::_1);
    }
};
